package cipher

/*
2017-06-13 Baseline Bench
BenchmarkEncodeCaesar-4       100000         19621 ns/op
BenchmarkDecodeCaesar-4       500000          3461 ns/op
BenchmarkNewShift-4         10000000           122 ns/op
BenchmarkEncodeShift-4         50000         38432 ns/op
BenchmarkDecodeShift-4        200000          7565 ns/op
BenchmarkNewVigenere-4        200000          6934 ns/op
BenchmarkEncVigenere-4        100000         12145 ns/op
BenchmarkDecVigenere-4        500000          2311 ns/op
*/

import (
	"regexp"
	"strings"
)

const testVersion = 1

type Cipher interface {
	Encode(string) string
	Decode(string) string
}

type Caesar struct {
	key []int
}

var LETTERS = regexp.MustCompile(`[a-z]+`)

func (c Caesar) Encode(input string) string {
	original := []rune(strings.Join(LETTERS.FindAllString(strings.ToLower(input), -1), ""))
	for i := 0; i < len(original); i++ {
		original[i] += rune(c.key[i%len(c.key)])
		if original[i] > 'z' {
			original[i] -= 26
		}
		if original[i] < 'a' {
			original[i] += 26
		}
	}
	return string(original)
}

func (c Caesar) Decode(input string) string {
	original := []rune(input)
	for i := 0; i < len(original); i++ {
		original[i] -= rune(c.key[i%len(c.key)])
		if original[i] > 'z' {
			original[i] -= 26
		}
		if original[i] < 'a' {
			original[i] += 26
		}
	}

	return string(original)
}

func NewCaesar() Cipher {
	return Caesar{[]int{3}}
}

func NewShift(input int) Cipher {
	if input <= -26 || input >= 26 || input == 0 {
		return nil
	}
	return Caesar{[]int{input}}
}
func NewVigenere(input string) Cipher {
	if len(input) == 0 {
		return nil
	}

	if strings.Count(input, "a") == len(input) {
		return nil
	}

	if len(strings.Join(LETTERS.FindAllString(input, -1), "")) != len(input) {
		return nil
	}

	original := []rune(input)

	key := make([]int, len(original))
	for i := 0; i < len(original); i++ {
		key[i] = int(original[i] - rune(97))
	}

	return Caesar{key}
}
